﻿using System;
using System.Collections;
using System.Drawing;
using System.IO;

namespace MJTop.NGif
{
    public class GifDecoder
    {
        protected int[] act;

        protected int bgColor;

        protected int bgIndex;

        protected Bitmap bitmap;

        protected byte[] block = new byte[256];

        protected int blockSize;

        protected int delay;

        protected int dispose;

        protected int frameCount;

        protected ArrayList frames;

        protected int[] gct;

        protected bool gctFlag;

        protected int gctSize;

        protected int height;

        protected int ih;

        protected Image image;

        protected Stream inStream;

        protected bool interlace;

        protected int iw;

        protected int ix;

        protected int iy;

        protected int lastBgColor;

        protected int lastDispose;

        protected Image lastImage;

        protected Rectangle lastRect;

        protected int[] lct;

        protected bool lctFlag;

        protected int lctSize;

        protected int loopCount = 1;

        protected readonly static int MaxStackSize;

        protected int pixelAspect;

        protected byte[] pixels;

        protected byte[] pixelStack;

        protected short[] prefix;

        protected int status;

        public readonly static int STATUS_FORMAT_ERROR;

        public readonly static int STATUS_OK;

        public readonly static int STATUS_OPEN_ERROR;

        protected byte[] suffix;

        protected int transIndex;

        protected bool transparency;

        protected int width;

        static GifDecoder()
        {
            GifDecoder.MaxStackSize = 4096;
            GifDecoder.STATUS_FORMAT_ERROR = 1;
            GifDecoder.STATUS_OK = 0;
            GifDecoder.STATUS_OPEN_ERROR = 2;
        }

        public GifDecoder()
        {
        }

        protected void DecodeImageData()
        {
            int i;
            int num = -1;
            int num1 = this.iw * this.ih;
            if ((this.pixels == null ? true : (int)this.pixels.Length < num1))
            {
                this.pixels = new byte[num1];
            }
            if (this.prefix == null)
            {
                this.prefix = new short[GifDecoder.MaxStackSize];
            }
            if (this.suffix == null)
            {
                this.suffix = new byte[GifDecoder.MaxStackSize];
            }
            if (this.pixelStack == null)
            {
                this.pixelStack = new byte[GifDecoder.MaxStackSize + 1];
            }
            int num2 = this.Read();
            int num3 = 1 << (num2 & 31);
            int num4 = num3 + 1;
            int num5 = num3 + 2;
            int num6 = num;
            int num7 = num2 + 1;
            int num8 = (1 << (num7 & 31)) - 1;
            for (i = 0; i < num3; i++)
            {
                this.prefix[i] = 0;
                this.suffix[i] = (byte)i;
            }
            int num9 = 0;
            int num10 = num9;
            int num11 = num9;
            int num12 = num9;
            int num13 = num9;
            int num14 = num9;
            int num15 = num9;
            int num16 = num9;
            int j = 0;
            while (j < num1)
            {
                if (num12 == 0)
                {
                    if (num15 >= num7)
                    {
                        i = num16 & num8;
                        num16 = num16 >> (num7 & 31);
                        num15 -= num7;
                        if ((i > num5 ? true : i == num4))
                        {
                            break;
                        }
                        else if (i == num3)
                        {
                            num7 = num2 + 1;
                            num8 = (1 << (num7 & 31)) - 1;
                            num5 = num3 + 2;
                            num6 = num;
                            continue;
                        }
                        else if (num6 != num)
                        {
                            int num17 = i;
                            if (i == num5)
                            {
                                int num18 = num12;
                                num12 = num18 + 1;
                                this.pixelStack[num18] = (byte)num13;
                                i = num6;
                            }
                            while (i > num3)
                            {
                                int num19 = num12;
                                num12 = num19 + 1;
                                this.pixelStack[num19] = this.suffix[i];
                                i = this.prefix[i];
                            }
                            num13 = this.suffix[i] & 255;
                            if (num5 < GifDecoder.MaxStackSize)
                            {
                                int num20 = num12;
                                num12 = num20 + 1;
                                this.pixelStack[num20] = (byte)num13;
                                this.prefix[num5] = (short)num6;
                                this.suffix[num5] = (byte)num13;
                                num5++;
                                if (((num5 & num8) != 0 ? false : num5 < GifDecoder.MaxStackSize))
                                {
                                    num7++;
                                    num8 += num5;
                                }
                                num6 = num17;
                            }
                            else
                            {
                                break;
                            }
                        }
                        else
                        {
                            int num21 = num12;
                            num12 = num21 + 1;
                            this.pixelStack[num21] = this.suffix[i];
                            num6 = i;
                            num13 = i;
                            continue;
                        }
                    }
                    else
                    {
                        if (num14 == 0)
                        {
                            num14 = this.ReadBlock();
                            if (num14 > 0)
                            {
                                num10 = 0;
                            }
                            else
                            {
                                break;
                            }
                        }
                        num16 = num16 + ((this.block[num10] & 255) << (num15 & 31));
                        num15 += 8;
                        num10++;
                        num14--;
                        continue;
                    }
                }
                num12--;
                int num22 = num11;
                num11 = num22 + 1;
                this.pixels[num22] = this.pixelStack[num12];
                j++;
            }
            for (j = num11; j < num1; j++)
            {
                this.pixels[j] = 0;
            }
        }

        protected bool Error()
        {
            return this.status != GifDecoder.STATUS_OK;
        }

        public int GetDelay(int n)
        {
            this.delay = -1;
            if ((n < 0 ? false : n < this.frameCount))
            {
                this.delay = ((GifDecoder.GifFrame)this.frames[n]).delay;
            }
            return this.delay;
        }

        public Image GetFrame(int n)
        {
            Image item = null;
            if ((n < 0 ? false : n < this.frameCount))
            {
                item = ((GifDecoder.GifFrame)this.frames[n]).image;
            }
            return item;
        }

        public int GetFrameCount()
        {
            return this.frameCount;
        }

        public Size GetFrameSize()
        {
            return new Size(this.width, this.height);
        }

        public Image GetImage()
        {
            return this.GetFrame(0);
        }

        public int GetLoopCount()
        {
            return this.loopCount;
        }

        private int[] GetPixels(Bitmap bitmap)
        {
            int[] r = new int[3 * this.image.Width * this.image.Height];
            int num = 0;
            for (int i = 0; i < this.image.Height; i++)
            {
                for (int j = 0; j < this.image.Width; j++)
                {
                    Color pixel = bitmap.GetPixel(j, i);
                    r[num] = pixel.R;
                    num++;
                    r[num] = pixel.G;
                    num++;
                    r[num] = pixel.B;
                    num++;
                }
            }
            return r;
        }

        protected void Init()
        {
            this.status = GifDecoder.STATUS_OK;
            this.frameCount = 0;
            this.frames = new ArrayList();
            this.gct = null;
            this.lct = null;
        }

        protected int Read()
        {
            int num = 0;
            try
            {
                num = this.inStream.ReadByte();
            }
            catch (IOException oException)
            {
                this.status = GifDecoder.STATUS_FORMAT_ERROR;
            }
            return num;
        }

        public int Read(Stream inStream)
        {
            this.Init();
            if (inStream == null)
            {
                this.status = GifDecoder.STATUS_OPEN_ERROR;
            }
            else
            {
                this.inStream = inStream;
                this.ReadHeader();
                if (!this.Error())
                {
                    this.ReadContents();
                    if (this.frameCount < 0)
                    {
                        this.status = GifDecoder.STATUS_FORMAT_ERROR;
                    }
                }
                inStream.Close();
            }
            return this.status;
        }

        public int Read(string name)
        {
            this.status = GifDecoder.STATUS_OK;
            try
            {
                name = name.Trim().ToLower();
                this.status = this.Read((new FileInfo(name)).OpenRead());
            }
            catch (IOException oException)
            {
                this.status = GifDecoder.STATUS_OPEN_ERROR;
            }
            return this.status;
        }

        protected int ReadBlock()
        {
            int num;
            this.blockSize = this.Read();
            int num1 = 0;
            if (this.blockSize > 0)
            {
                try
                {
                    int num2 = 0;
                    while (num1 < this.blockSize)
                    {
                        num2 = this.inStream.Read(this.block, num1, this.blockSize - num1);
                        if (num2 != -1)
                        {
                            num1 += num2;
                        }
                        else
                        {
                            goto Label0;
                        }
                    }
                }
                catch (IOException oException)
                {
                }
                Label0:
                if (num1 < this.blockSize)
                {
                    this.status = GifDecoder.STATUS_FORMAT_ERROR;
                }
                num = num1;
            }
            else
            {
                num = num1;
            }
            return num;
        }

        protected int[] ReadColorTable(int ncolors)
        {
            int[] numArray;
            int num = 3 * ncolors;
            int[] numArray1 = null;
            byte[] numArray2 = new byte[num];
            int num1 = 0;
            try
            {
                num1 = this.inStream.Read(numArray2, 0, (int)numArray2.Length);
            }
            catch (IOException oException)
            {
            }
            if (num1 >= num)
            {
                numArray1 = new int[256];
                int num2 = 0;
                int num3 = 0;
                while (num2 < ncolors)
                {
                    int num4 = num3;
                    num3 = num4 + 1;
                    int num5 = numArray2[num4] & 255;
                    int num6 = num3;
                    num3 = num6 + 1;
                    int num7 = numArray2[num6] & 255;
                    int num8 = num3;
                    num3 = num8 + 1;
                    int num9 = numArray2[num8] & 255;
                    int num10 = num2;
                    num2 = num10 + 1;
                    numArray1[num10] = -16777216 | num5 << 16 | num7 << 8 | num9;
                }
                numArray = numArray1;
            }
            else
            {
                this.status = GifDecoder.STATUS_FORMAT_ERROR;
                numArray = numArray1;
            }
            return numArray;
        }

        protected void ReadContents()
        {
            bool flag = false;
            while (true)
            {
                if ((flag ? true : this.Error()))
                {
                    break;
                }
                int num = this.Read();
                if (num <= 33)
                {
                    if (num == 0)
                    {
                        continue;
                    }
                    else if (num == 33)
                    {
                        int num1 = this.Read();
                        if (num1 == 249)
                        {
                            this.ReadGraphicControlExt();
                            continue;
                        }
                        else if (num1 == 255)
                        {
                            this.ReadBlock();
                            string str = "";
                            for (int i = 0; i < 11; i++)
                            {
                                char chr = (char)this.block[i];
                                str = string.Concat(str, chr.ToString());
                            }
                            if (!str.Equals("NETSCAPE2.0"))
                            {
                                this.Skip();
                            }
                            else
                            {
                                this.ReadNetscapeExt();
                            }
                            continue;
                        }
                        else
                        {
                            this.Skip();
                            continue;
                        }
                    }
                }
                else if (num == 44)
                {
                    this.ReadImage();
                    continue;
                }
                else if (num == 59)
                {
                    flag = true;
                    continue;
                }
                this.status = GifDecoder.STATUS_FORMAT_ERROR;
            }
        }

        protected void ReadGraphicControlExt()
        {
            this.Read();
            int num = this.Read();
            this.dispose = (num & 28) >> 2;
            if (this.dispose == 0)
            {
                this.dispose = 1;
            }
            this.transparency = (num & 1) != 0;
            this.delay = this.ReadShort() * 10;
            this.transIndex = this.Read();
            this.Read();
        }

        protected void ReadHeader()
        {
            string str = "";
            for (int i = 0; i < 6; i++)
            {
                char chr = (char)this.Read();
                str = string.Concat(str, chr.ToString());
            }
            if (str.StartsWith("GIF"))
            {
                this.ReadLSD();
                if ((!this.gctFlag ? false : !this.Error()))
                {
                    this.gct = this.ReadColorTable(this.gctSize);
                    this.bgColor = this.gct[this.bgIndex];
                }
            }
            else
            {
                this.status = GifDecoder.STATUS_FORMAT_ERROR;
            }
        }

        protected void ReadImage()
        {
            this.ix = this.ReadShort();
            this.iy = this.ReadShort();
            this.iw = this.ReadShort();
            this.ih = this.ReadShort();
            int num = this.Read();
            this.lctFlag = (num & 128) != 0;
            this.interlace = (num & 64) != 0;
            this.lctSize = 2 << (num & 7 & 31);
            if (!this.lctFlag)
            {
                this.act = this.gct;
                if (this.bgIndex == this.transIndex)
                {
                    this.bgColor = 0;
                }
            }
            else
            {
                this.lct = this.ReadColorTable(this.lctSize);
                this.act = this.lct;
            }
            int num1 = 0;
            if (this.transparency)
            {
                num1 = this.act[this.transIndex];
                this.act[this.transIndex] = 0;
            }
            if (this.act == null)
            {
                this.status = GifDecoder.STATUS_FORMAT_ERROR;
            }
            if (!this.Error())
            {
                this.DecodeImageData();
                this.Skip();
                if (!this.Error())
                {
                    this.frameCount++;
                    this.bitmap = new Bitmap(this.width, this.height);
                    this.image = this.bitmap;
                    this.SetPixels();
                    this.frames.Add(new GifDecoder.GifFrame(this.bitmap, this.delay));
                    if (this.transparency)
                    {
                        this.act[this.transIndex] = num1;
                    }
                    this.ResetFrame();
                }
            }
        }

        protected void ReadLSD()
        {
            this.width = this.ReadShort();
            this.height = this.ReadShort();
            int num = this.Read();
            this.gctFlag = (num & 128) != 0;
            this.gctSize = 2 << (num & 7 & 31);
            this.bgIndex = this.Read();
            this.pixelAspect = this.Read();
        }

        protected void ReadNetscapeExt()
        {
            bool flag;
            do
            {
                this.ReadBlock();
                if (this.block[0] == 1)
                {
                    int num = this.block[1] & 255;
                    int num1 = this.block[2] & 255;
                    this.loopCount = num1 << 8 | num;
                }
                flag = (this.blockSize <= 0 ? false : !this.Error());
            }
            while (flag);
        }

        protected int ReadShort()
        {
            int num = this.Read() | this.Read() << 8;
            return num;
        }

        protected void ResetFrame()
        {
            this.lastDispose = this.dispose;
            this.lastRect = new Rectangle(this.ix, this.iy, this.iw, this.ih);
            this.lastImage = this.image;
            this.lastBgColor = this.bgColor;
            this.lct = null;
        }

        protected void SetPixels()
        {
            int[] pixels = this.GetPixels(this.bitmap);
            if (this.lastDispose > 0)
            {
                if (this.lastDispose == 3)
                {
                    int num = this.frameCount - 2;
                    if (num <= 0)
                    {
                        this.lastImage = null;
                    }
                    else
                    {
                        this.lastImage = this.GetFrame(num - 1);
                    }
                }
                if (this.lastImage != null)
                {
                    Array.Copy(this.GetPixels(new Bitmap(this.lastImage)), 0, pixels, 0, this.width * this.height);
                    if (this.lastDispose == 2)
                    {
                        Graphics graphic = Graphics.FromImage(this.image);
                        Color empty = Color.Empty;
                        empty = (!this.transparency ? Color.FromArgb(this.lastBgColor) : Color.FromArgb(0, 0, 0, 0));
                        Brush solidBrush = new SolidBrush(empty);
                        graphic.FillRectangle(solidBrush, this.lastRect);
                        solidBrush.Dispose();
                        graphic.Dispose();
                    }
                }
            }
            int num1 = 1;
            int num2 = 8;
            int num3 = 0;
            for (int i = 0; i < this.ih; i++)
            {
                int num4 = i;
                if (this.interlace)
                {
                    if (num3 >= this.ih)
                    {
                        num1++;
                        switch (num1)
                        {
                            case 2:
                                {
                                    num3 = 4;
                                    break;
                                }
                            case 3:
                                {
                                    num3 = 2;
                                    num2 = 4;
                                    break;
                                }
                            case 4:
                                {
                                    num3 = 1;
                                    num2 = 2;
                                    break;
                                }
                        }
                    }
                    num4 = num3;
                    num3 += num2;
                }
                num4 += this.iy;
                if (num4 < this.height)
                {
                    int num5 = num4 * this.width;
                    int num6 = num5 + this.ix;
                    int num7 = num6 + this.iw;
                    if (num5 + this.width < num7)
                    {
                        num7 = num5 + this.width;
                    }
                    int num8 = i * this.iw;
                    while (num6 < num7)
                    {
                        int num9 = num8;
                        num8 = num9 + 1;
                        int num10 = this.pixels[num9] & 255;
                        int num11 = this.act[num10];
                        if (num11 != 0)
                        {
                            pixels[num6] = num11;
                        }
                        num6++;
                    }
                }
            }
            this.SetPixels(pixels);
        }

        private void SetPixels(int[] pixels)
        {
            int num = 0;
            for (int i = 0; i < this.image.Height; i++)
            {
                for (int j = 0; j < this.image.Width; j++)
                {
                    int num1 = num;
                    num = num1 + 1;
                    Color color = Color.FromArgb(pixels[num1]);
                    this.bitmap.SetPixel(j, i, color);
                }
            }
        }

        protected void Skip()
        {
            bool flag;
            do
            {
                this.ReadBlock();
                flag = (this.blockSize <= 0 ? false : !this.Error());
            }
            while (flag);
        }

        public class GifFrame
        {
            public int delay;

            public Image image;

            public GifFrame(Image im, int del)
            {
                this.image = im;
                this.delay = del;
            }
        }
    }
}